﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.IO;
using MatthiWare.PortChecker.UI;
using MatthiWare.PortChecker.Events;

namespace MatthiWare.PortChecker
{
    public delegate void StatusCallback(String status);

    public partial class frmMain : Form
    {
        PortCollection portCollection = new PortCollection();
        SettingsDialog sd = new SettingsDialog();
        CheckPortDialog cpd;
        PortChecker portChecker;
        delegate void PortAdderDelegate(PortInfo o);
        Int32 portsToCheck = 0;
        Int32 portsChecked = 0;
        object syncObject = new object();
        bool searcher = false;
        bool aborting = false;

        public frmMain()
        {
            InitializeComponent();

            cpd = new CheckPortDialog(portCollection);
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            if (searcher)
            {
                searcher = false;
                aborting = true;

                btnSearch.Text = "Check Ports";

                Status("Aborting...");

                if (portChecker != null)
                    portChecker.End();

                txtPortRange1.Enabled = true;
                txtPortRange2.Enabled = true;
            }
            else
            {
                aborting = false;
                searcher = true;

                UInt16 minPortRange, maxPortRange = 1024;

                bool parse1 = !UInt16.TryParse(txtPortRange1.Text, out minPortRange);
                bool parse2 = !UInt16.TryParse(txtPortRange2.Text, out maxPortRange);

                if (parse1 || parse2)
                {
                    MessageBox.Show(String.Format("Unable to parse port numbers\nThe value should be between {0}-{1}", UInt16.MinValue, UInt16.MaxValue), "Parsing error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
                else if (minPortRange > maxPortRange)
                {
                    MessageBox.Show("The minimum port range can't be higher then the maximum port range", "Max port less then min port error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }

                portsChecked = 0;
                portsToCheck = maxPortRange - minPortRange;
                txtPortRange1.Enabled = false;
                txtPortRange2.Enabled = false;

                lbPortsClosed.Items.Clear();
                lbPortsOpen.Items.Clear();

                portChecker = new PortChecker(minPortRange, maxPortRange, sd.GetNewIp());

                portChecker.PortCheckCompleted += new EventHandler<PortEventArgs>(portChecker_PortCheckCompleted);
                portChecker.StartChecking(new StatusCallback(Status));

                lblStatus.Text = "Connecting to port(s)...";

                btnSearch.Text = "Abort";
            }
        }

        void Status(String status)
        {
            if (aborting)
                lblStatus.Text = "Status: Aborting...";
            else
                lblStatus.Text = "Status: " + status;
        }

        void portChecker_PortCheckCompleted(object sender, PortEventArgs e)
        {
            lock (syncObject)
                portsChecked += 1;

            PortInfo pInfo = new PortInfo(portCollection.GetPortInfo(e.Port), e.Open, e.Message);

            if (e.Open)
                AddOpenPort(pInfo);
            else
                AddClosedPort(pInfo);

            if (portsChecked >= portsToCheck)
            {
                Status("All ports are checked!");

                if (txtPortRange1.InvokeRequired)
                    txtPortRange1.Invoke(new MethodInvoker(() => { txtPortRange1.Enabled = true; }));
                else
                    txtPortRange1.Enabled = true;

                if (txtPortRange2.InvokeRequired)
                    txtPortRange2.Invoke(new MethodInvoker(() => { txtPortRange2.Enabled = true; }));
                else
                    txtPortRange2.Enabled = true;

                if (btnSearch.InvokeRequired)
                    btnSearch.Invoke(new MethodInvoker(() => { btnSearch.Text = "Check Ports"; }));
                else
                    btnSearch.Text = "Check Ports";
                
                searcher = false;
            }
        }

        void AddOpenPort(PortInfo obj)
        {
            if (lbPortsOpen.InvokeRequired)
                lbPortsOpen.Invoke(new PortAdderDelegate(AddOpenPort), obj);
            else
            {
                lbPortsOpen.Items.Add(obj);

                lbPortsOpen.SelectedIndex = lbPortsOpen.Items.Count - 1;
            }
        }

        void AddClosedPort(PortInfo obj)
        {
            if (lbPortsClosed.InvokeRequired)
                lbPortsClosed.Invoke(new PortAdderDelegate(AddClosedPort), obj);
            else
            {
                lbPortsClosed.Items.Add(obj);

                lbPortsClosed.SelectedIndex = lbPortsClosed.Items.Count - 1;
            }
        }

        private void checkPortToolStripMenuItem_Click(object sender, EventArgs e)
        {
            cpd.ShowDialog();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "Save report!";
            sfd.SupportMultiDottedExtensions = true;
            sfd.ValidateNames = true;
            sfd.OverwritePrompt = true;
            sfd.InitialDirectory = Application.StartupPath;
            sfd.Filter = "Xml Files (*.xml)|*.xml";

            if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                SaveAsXml(sfd.FileName);
            }

            if (sfd != null)
                sfd.Dispose(); sfd = null;
        }

        private void SaveAsXml(String path)
        {
            PortInfo[] _closed = new PortInfo[lbPortsClosed.Items.Count];
            PortInfo[] _open = new PortInfo[lbPortsOpen.Items.Count];

            lbPortsOpen.Items.CopyTo(_open, 0);
            lbPortsClosed.Items.CopyTo(_closed, 0);

            SAVE_REPORT sr = new SAVE_REPORT(_open, _closed);

            FileStream fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write);
            XmlSerializer xml = new XmlSerializer(typeof(SAVE_REPORT));
            xml.Serialize(fs, sr);
            fs.Close();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            CloseHandler();
        }

        private void CloseHandler()
        {
            if (portChecker != null)
            {
                bool wasRunning = portChecker._running;

                portChecker.End();

                if (wasRunning)
                {
                    MessageBox.Show("All pending port checks have been canceled\nThe process might still be running till all of the tasks are done", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox1 ab = new AboutBox1();
            ab.ShowDialog();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            sd.ShowDialog();
            cpd.SetIp(sd.GetStrIp());
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            cpd.SetIp(sd.GetStrIp());
        }

        private void txtPortRange2_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                btnSearch.PerformClick();
        }

        private void txtPortRange1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                txtPortRange2.Focus();
        }

        private void lbPortsOpen_DoubleClick(object sender, EventArgs e)
        {
            if (lbPortsOpen.SelectedItem != null)
            {
                PortInfo pinfo = (PortInfo)lbPortsOpen.SelectedItem;

                PortInfoDialog pinfoDialog = new PortInfoDialog(pinfo);
                pinfoDialog.Show(this);
            }

        }

        private void lbPortsClosed_DoubleClick(object sender, EventArgs e)
        {
            if (lbPortsClosed.SelectedItem != null)
            {
                PortInfo pinfo = (PortInfo)lbPortsClosed.SelectedItem;

                PortInfoDialog pinfoDialog = new PortInfoDialog(pinfo);
                pinfoDialog.Show(this);
            }
        }

    }

    
}
